package com.maven.demo.core.common.cookie;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.maven.demo.core.common.SignUtil;

public class CookieManager {
	private static final Logger LOG = LoggerFactory.getLogger(CookieManager.class);
	private HttpServletRequest request;
	private String cookieTempName;
	private static final String UTF_8 = "UTF-8";
	private static final String ISO_8859_1 = "ISO-8859-1";
	
	public CookieManager(HttpServletRequest request, String cookieTempName) {
		this.request = request;
		this.cookieTempName = cookieTempName;
	}
	
	public CookieManager (HttpServletRequest request) {
		this(request, "_default_temp_cookie_");
	}
	
	public String getValue(String cookieName,String key,boolean isEncrypted) {
		return getValue(cookieName, key, "",isEncrypted);
	}
	
	public String getValue(String cookieName,String key,String defaultValue,boolean isEncrypted) {
		Map<String,String> valueMap = parseCookie(cookieName,isEncrypted);
		String value = valueMap.get(key).toString();
		return (SignUtil.isEmpty(value)) ? defaultValue : value;
	}

	@SuppressWarnings("unchecked")
	private Map<String, String> parseCookie(String cookieName, boolean isEncrypted) {
		Map<String,String> valueMap = (Map<String,String>)getRequest().getAttribute(cookieName);
		if(valueMap != null) {
			return valueMap;
		}
		
		valueMap = new HashMap<String, String>();
		getRequest().setAttribute(cookieName, valueMap);
		String cookieValue = getCookieValue(cookieName,isEncrypted);
		if(SignUtil.isEmpty(cookieValue)){
			return valueMap;
		}
		
		String[] kvPairs = cookieValue.split("&");
		for (String kvPair : kvPairs) {
			if(SignUtil.isNotEmpty(kvPair)) {
				int offset = kvPair.indexOf(61);
				if(offset > 0) {
					String key = kvPair.substring(0, offset);
					String value = kvPair.substring(offset + 1);
					if(SignUtil.isEmpty(key) || SignUtil.isEmpty(value)) {
						continue;
					}
					
					try {
						valueMap.put(key, URLDecoder.decode(value, UTF_8));
					} catch (UnsupportedEncodingException e) {
						LOG.error("failed to put key and value:" + key + "---" + value,e);
					}
				}
			}
		}
		return valueMap;
	}

	private String getCookieValue(String cookieName, boolean isEncrypted) {
		String cookieValue = getCookieValue(cookieName, null);
		if(cookieValue == null) {
			return null;
		}
		
		String decodedValue = null;
		try {
			decodedValue = URLDecoder.decode(cookieValue, ISO_8859_1);
		} catch (UnsupportedEncodingException e) {
			LOG.error("failed to decode cookie value:" + cookieValue,e);
			return null;
		}
		
		if(isEncrypted) {
			try {
				byte[] decodeValueBytes = decodedValue.getBytes(ISO_8859_1);
				byte[] decryptedBytes = new Encrypter().decrypt(decodeValueBytes);
				return new String(decryptedBytes,UTF_8);
			} catch (UnsupportedEncodingException e) {
				LOG.error("failed to decrypted cookie value:" + cookieValue,e);
				return null;
			}
		}
		return decodedValue;
	}

	private String getCookieValue(String cookieName, String defaultValue) {
		Cookie[] cookies = getRequest().getCookies();
		if(cookies != null) {
			for (Cookie cookie : cookies) {
				if(cookieName.equals(cookie.getName())) {
					return cookie.getValue();
				}
			}
		}
		return defaultValue;
	}
	
	public void save(HttpServletResponse response,String cookieName,boolean isEncrypted) {
		save(response, cookieName, null ,isEncrypted);
	}

	public void save(HttpServletResponse response, String cookieName, String domain, boolean isEncrypted) {
		save(response, cookieName, domain, "/", isEncrypted);
	}

	public void save(HttpServletResponse response, String cookieName, String domain, String path, boolean isEncrypted) {
		save(response, cookieName, domain, path, -1, isEncrypted);
	}

	public void save(HttpServletResponse response, String cookieName, String domain, String path, int age, boolean isEncrypted) {
		Map<String, String> valueMap = parseCookie(cookieName, isEncrypted);
		StringBuffer sb = new StringBuffer();
		
		for (Map.Entry<String, String> e : valueMap.entrySet()) {
			String key = e.getKey().toString();
			if(SignUtil.isNotEmpty(key)) {
				String value = e.getValue().toString();
				if(SignUtil.isNotEmpty(value)) {
					if(sb.length() > 0) {
						sb.append("&");
					}
					sb.append(key);
					sb.append("==");
					try {
						sb.append(URLEncoder.encode(value, UTF_8));
					} catch (UnsupportedEncodingException e1) {
						LOG.error("failed to encode value:" + value,e);
					}
				}
			}
		}
		
		addCookie(response,cookieName,sb.toString(),domain,path,age,isEncrypted);
	}

	private void addCookie(HttpServletResponse response, String cookieName, String cookieValue, String domain, String path, int age, boolean isEncrypted) {
		Cookie cookie = null;
		
		if(isEncrypted) {
			String encryptedValue;
			
			try {
				encryptedValue = new String(new Encrypter().encrypt(cookieValue.getBytes(UTF_8)),ISO_8859_1);
				
				encryptedValue = URLEncoder.encode(encryptedValue, ISO_8859_1);
			} catch (UnsupportedEncodingException e) {
				LOG.error("failed to encode value:" + cookieValue,e);
				return;
			}
			
			cookie = new Cookie(cookieName, encryptedValue);
		}else {
			try {
				
				cookieValue = URLEncoder.encode(cookieValue, ISO_8859_1);
			} catch (UnsupportedEncodingException e) {
				LOG.error("failed to encode value:" + cookieValue,e);
				return;
			}
			
			cookie = new Cookie(cookieName, cookieValue);
		}
		
		cookie.setPath(path);
		cookie.setMaxAge(age);
		if(SignUtil.isNotEmpty(domain)) {
			cookie.setDomain(domain);
		}
		response.addCookie(cookie);
	}
	
	public void cleanAll(HttpServletResponse response,String cookieName,boolean isEncrypted) {
		save(response, cookieName, null, "/", 0, isEncrypted);
	}
	
	public void setLogValue(String cookieName,String key,String value) {
		Map<String, String> valueMap = parseCookie(cookieName, false);
		
		if(SignUtil.isNotEmpty(value)) {
			valueMap.put(key, value);
		}else {
			valueMap.remove(key);
		}
	}
	
	public void setValue(String cookieName,String key,String value,boolean isEncrypted) {
		Map<String, String> valueMap = parseCookie(cookieName, isEncrypted);
		
		if(SignUtil.isNotEmpty(value)) {
			valueMap.put(key, value);
		}else {
			valueMap.remove(key);
		}
	}

	public HttpServletRequest getRequest() {
		return request;
	}

	public String getCookieTempName() {
		return cookieTempName;
	}

	public void setCookieTempName(String cookieTempName) {
		this.cookieTempName = cookieTempName;
	}
}
